package rpc

/*
基于 HTTP 协议的 RPC 服务器，用于接收和处理客户端的远程方法调用（Call）和广播请求（Broadcast）。
它通过注册 "路由 - 处理器" 映射关系，将不同业务类型的请求分发到对应的处理函数，是分布式系统中服务端处理 RPC 通信的核心组件。

提供 HTTP 服务端点：通过/rpc/call和/rpc/broadcast两个 HTTP 接口，接收客户端的 RPC 请求；
路由请求到业务逻辑：通过 " 路由 ID（route）- 处理器函数 " 的映射，将不同业务类型的请求（如玩家操作、聊天消息）分发到对应的处理函数；
处理请求并返回响应：对Call请求执行处理器函数并返回结果，对Broadcast请求异步通知相关处理器，完成集群内消息同步。

是一个桥梁，负责接收Call和Broadcast内容，通过路由去接收回复

2025/10/16
去除默认http多路复用，节点之间是独立的
*/
import (
	"context"
	"encoding/json"
	"fmt"
	"gameServer/log"
	"net/http"
	"sync"
	"time"
)

/* //* RPC服务器实现，提供/rpc/call和/rpc/broadcast接口，通过注册路由-处理器映射处理远程调用请求，支持并发安全和优雅关闭 */

// RPC 服务器
type Server struct {
	port     int                                          // 服务器监听端口（如52051） （服务器这么监听，客户端访问ip:port）
	handlers map[uint32]func(data []byte) ([]byte, error) // 路由-处理器映射表：key=路由ID，value=处理函数
	mu       sync.RWMutex                                 // 读写锁（sync.RWMutex类型），用于保护handlers映射表的并发安全
	server   *http.Server                                 // HTTP服务实例（*http.Server类型），替代直接用http.ListenAndServe，支持优雅关闭、配置管理
	mux      *http.ServeMux                               // HTTP多路复用器（*http.ServeMux类型），每个RPC Server独占一个，解决全局路由重复注册问题
}

// 创建RPC服务器
func NewServer(port int) *Server {
	mux := http.NewServeMux()
	return &Server{
		port:     port,
		handlers: make(map[uint32]func(data []byte) ([]byte, error)),
		mux:      mux,
	}
}

// 注册路由处理器（一个处理器就是一个函数，参数是二进制数据）
func (s *Server) RegisterHandler(route uint32, handler func(data []byte) ([]byte, error)) {
	s.mu.Lock()
	s.handlers[route] = handler
	defer s.mu.Unlock()
	/* 示例
	// 注册"玩家等级查询"处理器（路由100）
	server.RegisterHandler(100, func(data []byte) ([]byte, error) {
	    // 解析data（如玩家ID）→ 查询等级 → 返回结果
	    return []byte(`{"level": 99}`), nil
	})
	*/

}

// 启动RPC服务器
// 用 mux（独立路由表）和 server（HTTP 服务）启动服务
func (s *Server) Start() error {
	// 这不还是路由，，，这下倒是回到http的路由了
	// 一般比较原始的自定义路由方法用这个，网页那种不用，有gin框架
	// // 注册HTTP路由：将"/rpc/call"请求映射到s.handleCall方法，"/rpc/broadcast"映射到s.handleBroadcast方法
	//向当前Server的独立mux注册路由，而非全局http.DefaultServeMux
	// 语法：调用s.mux.HandleFunc（专属路由表），绑定“路径→处理方法”
	// 功能：/rpc/call路径的请求，由s.handleCall方法处理；/rpc/broadcast由s.handleBroadcast处理
	s.mux.HandleFunc("/rpc/call", s.handleCall)
	s.mux.HandleFunc("/rpc/broadcast", s.handleBroadcast)
	// 创建http服务器
	s.server = &http.Server{
		Addr:    fmt.Sprintf(":%d", s.port),
		Handler: s.mux, // 核心：指定用当前Server的独立mux处理请求，而非默认的DefaultServeMux
	}

	log.Infof("RPC server started on port %d", s.port)
	// 启动HTTP服务：监听指定端口（格式":port"），使用默认的HTTP多路复用器 该函数会阻塞运行，直到服务停止。（这里开始就有多个服务同时开始了）
	// 参数1：监听地址，格式为":端口号"（如":8080"表示监听所有IP的8080端口）
	// 参数2：HTTP处理器，nil表示使用默认的多路复用器（DefaultServeMux）
	// 该函数会阻塞当前协程，直到服务器停止（如收到中断信号）

	// 这里就不能用默认的多路复用器了
	// 语法：*http.Server的ListenAndServe()方法，阻塞当前协程，直到服务停止
	// 功能：启动监听，接收客户端请求，由s.mux分发到对应的处理方法（s.handleCall/s.handleBroadcast）
	// 错误处理：只返回非“服务关闭”的错误（err != http.ErrServerClosed）

	//彻底隔离路由

	if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		return err
	}
	return nil
}

// 停止RPC服务器
func (s *Server) Stop() error {
	if s.server != nil {
		//创建带超时的context：控制优雅关闭的最大等待时间（5秒）
		// 语法：context.WithTimeout返回“超时上下文”和“取消函数”，defer cancel()确保资源释放
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
		/*
			context.Background()：是 “根”，提供最基础的空白上下文；
			ctx：是 “子”，基于根上下文加上 “5 秒超时” 规则，给Shutdown函数传递 “超时限制” 和 “取消信号”；
			cancel：是 “遥控器”，用来手动触发ctx的取消，同时defer cancel()确保 “任务结束必关遥控器”，不浪费资源。
		*/
		defer cancel()
		// 功能：① 停止接收新请求；② 等待现有请求处理完（最长5秒）；③ 关闭监听端口
		// 原理：依赖context控制超时，超过5秒未处理完的请求会被强制关闭，平衡“等待时间”和“服务可用性”
		return s.server.Shutdown(ctx)
	}
	return nil
}

// 处理 RPC 调用（客户端的Call请求）
//
//	w http.ResponseWriter：响应写入器，用于向客户端发送响应（状态码、响应体等）
//	r *http.Request：请求对象，包含客户端发送的所有信息（方法、URL、请求体等）
//	都是自动发送逻辑，执行函数以后就可以，不需要手动发
func (s *Server) handleCall(w http.ResponseWriter, r *http.Request) {
	// 检查请求方法：只允许POST（RPC调用通常用POST传输数据，POST给服务器）
	if r.Method != http.MethodPost {
		// 参数1：响应写入器
		// 参数2：错误消息
		// 参数3：HTTP状态码（405 Method Not Allowed表示不允许该方法）
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	var msg Message
	// 解析请求体
	if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
		// 解析失败，json格式错误之类，返回400 bad request
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// 根据请求中的Route（路由ID）从handlers映射表中查找对应的处理器函数
	// handler：找到的处理器函数；exists：bool类型，标识是否找到
	s.mu.RLock()
	handler, exists := s.handlers[msg.Route]
	s.mu.RUnlock()
	if !exists {
		// 未找到对应处理器（如路由未注册），返回404错误（Not Found）
		http.Error(w, fmt.Sprintf("No handler for route %d", msg.Route), http.StatusNotFound)
		return
	}
	// 广播到对应处理器
	// 调用找到的处理器函数处理业务逻辑
	// 传入客户端发送的Data（二进制业务数据），获取处理结果和可能的错误
	result, err := handler(msg.Data)
	if err != nil {
		// 处理器执行出错（如业务逻辑异常），返回500错误（Internal Server Error 服务器内部错误）
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	// 响应消息
	response := Message{
		Route: msg.Route,
		Data:  result,
	}
	// 设置响应头的Content-Type为application/json，告知客户端响应数据是JSON格式
	w.Header().Set("Content-Type", "application/json")
	// 将响应消息序列化为JSON并写入响应体（通过w返回给客户端）
	// 这里用NewEncoder(w)完成网络回复，默认就是200，相当于之前的Printf写入文件什么的，某种情况下把w理解成文件描述符？
	json.NewEncoder(w).Encode(response)

	// 隐式提交，如果没写 w.WriteHeader，会自动调用这个函数

}

// 处理广播请求
//
//	w http.ResponseWriter：用于发送响应
//	r *http.Request：客户端请求信息
func (s *Server) handleBroadcast(w http.ResponseWriter, r *http.Request) {
	// 检查请求方法是否为POST，非POST则返回405错误
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}
	var msg Message
	// 把 Go 结构体 → 序列化为 JSON 文本（通过 HTTP 响应返回给客户端）。
	// 和InCoder输入源，输出目标相反
	if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	s.mu.RLock()
	// 接收到的内容广播到所有处理器
	for route, handler := range s.handlers {
		if route == msg.Route {
			// 用go关键字启动goroutine（协程）异步执行处理器函数
			// 目的：避免处理器执行耗时过长导致客户端等待超时（广播只需"发送成功"，无需等待结果）
			go handler(msg.Data)
		}
	}
	s.mu.RUnlock()
	// 向客户端返回200 OK状态码，告知广播请求已被服务器接收并处理，这里已经是在做发送操作了
	w.WriteHeader(http.StatusOK)
}
